#region Summary

// 
// Owner : JackieHan 
// 
// Solution : Skomart.Spider 
// 
// Project : Jackie.Utility
// 
// File : ObjectConversion.cs
// 
// CopyRight : Copyright © 2010-2011 Skomart Corporation, All Rights Reserved 
// 
// Summary : 提供方法把BusinessObject和ViewObject相互转换
// 
// History : 
//			2010-10-09 09:52:23	    Create 
// 

#endregion

#region Using

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Jackie.Utility.CustomException;
using Jackie.Utility.CustomExtension;
using Jackie.Utility.Spring;

#endregion

namespace Jackie.Utility.BvConversion
{
    /// <summary> 
    /// 提供方法把BusinessObject和ViewObject相互转换
    /// </summary>
    /// <typeparam name="TB">Business类型</typeparam>
    /// <typeparam name="TV">View类型</typeparam>
    public class ObjectConvertor<TB, TV>
        where TB : class
        where TV : class
    {

        private IValueConversionFactory valueConversionFactory;

        /// <summary>
        /// 默认构造函数
        /// </summary>
        public ObjectConvertor()
        {
            valueConversionFactory = new DefaultValueConversionFacotry();
        }

        /// <summary>
        /// 用自定的IValueConversionFactory初始化
        /// </summary>
        /// <param name="valueConversionFactory"></param>
        public ObjectConvertor(IValueConversionFactory valueConversionFactory)
        {
            this.valueConversionFactory = valueConversionFactory;
        }


        /// <summary>
        /// 根据配置文件把BusinessObjection转换成ViewObject
        /// </summary>
        /// <param name="businessObject">BusinessObject</param>
        /// <returns></returns>
        public TV ObjectOfBusinessToView(TB businessObject)
        {
            Type businessType = businessObject.GetType();
            Type viewType = typeof(TV);
            //获取配置
            ObjectConversionConfig objectConfig = GetObjectConfig(businessType, viewType);
            //创建空的ViewObject
            var viewObject = Activator.CreateInstance(viewType) as TV;
            //给各属性赋值
            objectConfig.PropertyConfigs.ForEach(item => SetViewPropertyValue(item, businessType, businessObject, viewType, viewObject));
            return viewObject;
        }

        /// <summary>
        /// 根据配置文件把BusinessObjection转换成ViewObject,并提供自定的操作
        /// </summary>
        /// <param name="business">BusinessObject</param>
        /// <param name="action">自定的操作</param>
        /// <returns></returns>
        public TV ObjectOfBusinessToView(TB business, Action<TB, TV> action)
        {
            var viewObject = ObjectOfBusinessToView(business);
            action(business, viewObject);
            return viewObject;
        }

        /// <summary>
        /// 根据配置文件把BusinessObjection集合转换成ViewObject集合,并提供自定的操作
        /// </summary>
        /// <param name="sorts">BusinessObjection集合</param>
        /// <param name="action">自定的操作</param>
        /// <returns></returns>
        public IList<TV> CollectionOfBusinessToView(IList<TB> sorts, Action<TB, TV> action)
        {
            IList<TV> tvs = new List<TV>();
            sorts.ForEach(item => tvs.Add(ObjectOfBusinessToView(item, action)));
            return tvs;
        }

        /// <summary>
        /// 根据配置文件把BusinessObjection集合转换成ViewObject集合
        /// </summary>
        /// <typeparam name="TB">Business类型</typeparam>
        /// <typeparam name="TV">View类型</typeparam>
        /// <param name="sorts">BusinessCollection</param>
        /// <returns></returns>
        public IList<TV> CollectionOfBusinessToView(IList<TB> sorts)
        {
            IList<TV> tvs = new List<TV>();
            sorts.ForEach(item => tvs.Add(ObjectOfBusinessToView(item)));
            return tvs;
        }

        #region Private Method For ObjectOfBusinessToView
        /// <summary>
        /// 设置ViewObject属性值
        /// </summary>
        private void SetViewPropertyValue(PropertyConfig item, Type businessType, TB businessObject, Type viewType, TV viewObject)
        {
            if (item.BusinessProperty.Contains("."))
            {
                SetViewSubClassPropertyValue(item, businessType, businessObject, viewType, viewObject);
            }
            else
            {
                SetViewNormalPropertyValue(item, businessType, businessObject, viewType, viewObject);
            }
        }
        /// <summary>
        /// 设置一般属性值
        /// </summary>
        private void SetViewNormalPropertyValue(PropertyConfig item, Type businessType, TB businessObject, Type viewType, TV viewObject)
        {
            PropertyInfo businessProperty =
                businessType.GetProperty(item.BusinessProperty);
            if (businessProperty == null)
                throw new NullReferenceException(string.Format("the business property[{0}] is configed error!",
                                                               item.BusinessProperty));
            PropertyInfo viewProperty =
                viewType.GetProperty(item.ViewProperty);
            if (viewProperty == null)
                throw new NullReferenceException(string.Format("the view property[{0}] is configed error!",
                                                               item.ViewProperty));
            TransAndSetViewObjectValue(viewProperty, businessProperty, viewObject, businessObject);
        }
        /// <summary>
        /// 设置复合属性值
        /// </summary>
        private void SetViewSubClassPropertyValue(PropertyConfig item, Type businessType, TB businessObject, Type viewType, TV viewObject)
        {
            Type subBusinessType = GetBusinessSubClassPropertyType(item, businessType);
            object businessPropertyValue = GetBusinessSubClassPropertyValue(item, businessType, businessObject);
            PropertyInfo viewProperty = viewType.GetProperty(item.ViewProperty);

            var valueConversion = valueConversionFactory.CreateValueConversion(subBusinessType,
                                                                               viewProperty.PropertyType);
            var viewValue = valueConversion.GetViewValue(businessPropertyValue);
            viewProperty.SetValue(viewObject, viewValue, null);
        }

        #endregion

        #region Private Method For Class

        private Type GetBusinessSubClassPropertyType(PropertyConfig item, Type businessType)
        {
            string[] propertys = item.BusinessProperty.Split('.');
            PropertyInfo businessProperty = businessType.GetProperty(propertys[0]);
            if (businessProperty != null)
            {
                Type subType = businessProperty.PropertyType;
                return subType.GetProperty(propertys[1]).PropertyType;
            }
            return null;
        }

        /// <summary>
        /// 获取Business对象包含子对象的属性值
        /// </summary>
        /// <param name="item"></param>
        /// <param name="businessType"></param>
        /// <param name="businessObject"></param>
        /// <returns></returns>
        private object GetBusinessSubClassPropertyValue(PropertyConfig item, Type businessType, TB businessObject)
        {
            string[] propertys = item.BusinessProperty.Split('.');
            PropertyInfo businessProperty = businessType.GetProperty(propertys[0]);
            object businessPropertyValue = businessProperty.GetValue(businessObject, null);
            if (businessPropertyValue != null)
            {
                Type subType = businessPropertyValue.GetType();
                var subBusinessProperty = subType.GetProperty(propertys[1]);
                businessPropertyValue = subBusinessProperty.GetValue(businessPropertyValue, null);
            }
            return businessPropertyValue;
        }

        /// <summary>
        /// 获取Business对象的属性
        /// </summary>
        /// <param name="item"></param>
        /// <param name="businessType"></param>
        /// <returns></returns>
        private PropertyInfo GetBusinessProperty(PropertyConfig item, Type businessType)
        {
            string[] propertys = item.BusinessProperty.Split('.');
            PropertyInfo businessProperty = businessType.GetProperty(propertys[0]);
            return businessProperty;
        }

        /// <summary>
        /// 根据BusinessType and ViewType 获取对性的配置文件  
        /// </summary>
        /// <param name="businessType">Business Type</param>
        /// <param name="viewType">View Type</param>
        /// <returns>配置</returns>
        private ObjectConversionConfig GetObjectConfig(Type businessType, Type viewType)
        {
            ConversionConfigurationManager configManager = ConversionConfigurationManager.Instance;
            var businessTypeName = businessType.FullName;
            if (businessTypeName.Contains("Proxy"))
                businessTypeName = businessType.BaseType.FullName;
            if (!configManager.BusinessObjectConfig.ContainsKey(businessTypeName))
                throw new NotConfigedException(
                    string.Format("the business type [fullName:{0} is not configed!]", businessTypeName), "Conversion");
            if (!configManager.BusinessObjectConfig[businessTypeName].ContainsKey(viewType.FullName))
                throw new NotConfigedException(
                    string.Format("the businessType[fullName:{0}] to viewType[fullName:{1}] doesn't configeed!",
                                  businessTypeName, viewType.FullName), "Conversion");
            var viewBusinessConfig = configManager.BusinessObjectConfig[businessTypeName][viewType.FullName];

            return viewBusinessConfig;
        }

        #endregion

        /// <summary>
        /// 根据配置文件把ViewObject转换成BusinessObject
        /// </summary>
        /// <typeparam name="TV">View Type</typeparam>
        /// <typeparam name="TB">Business Type</typeparam>
        /// <param name="viewObject">需要转换的ViewObject</param>
        /// <returns>Business Object</returns>
        public TB ObjectOfViewToBusiness(TV viewObject)
        {
            Type businessType = typeof(TB);
            Type viewType = viewObject.GetType();
            //查找配置
            ObjectConversionConfig conversionConfig = GetObjectConfig(businessType, viewType);
            //根据主键获取原来的BusinessObject对象
            var businessObject = GetBusinessObjectByPrimaryKey(businessType, viewType, viewObject, conversionConfig) as TB;
            //设置各属性的新值
            conversionConfig.PropertyConfigs.ForEach(item => SetBusinessPropertyValue(item, viewObject, businessObject, viewType, businessType));

            return businessObject;
        }

        #region Private Method For ObjectOfViewToBusiness

        /// <summary>
        /// 根据主键获取编辑前的值
        /// </summary>
        private TB GetBusinessObjectByPrimaryKey(Type businessType, Type viewType, TV viewObject, ObjectConversionConfig conversionConfig)
        {
            TB businessObject = null;
            var businessConversion = SpringHelper.Context.GetObjectById<IConversion>(businessType.Name + "Conversion");
            if (businessConversion == null)
                throw new Exception(string.Format("the business type[{0}Business] not implement the interface IConversion", businessType.Name));
            conversionConfig.PropertyConfigs.ForEach(item =>
                                                         {
                                                             if ((item.IsKey) && (businessObject == null))
                                                             {
                                                                 PropertyInfo viewProperty =
                                                                     viewType.GetProperty(item.ViewProperty);
                                                                 var keyValue = viewProperty.GetValue(viewObject, null);
                                                                 if (keyValue != null)
                                                                     businessObject = businessConversion.GetByPrimaryKey(keyValue.ToString()) as TB;
                                                             }
                                                         });
            return businessObject ?? (businessObject = Activator.CreateInstance(businessType) as TB);
        }

        /// <summary>
        /// 设置属性的新值
        /// </summary>
        private void SetBusinessPropertyValue(PropertyConfig item, TV viewObject, TB businessObject, Type viewType, Type businessType)
        {
            if (!item.BusinessProperty.Contains("."))
            {
                SetNormalBusinessPropertyValue(item, businessType, viewType, businessObject, viewObject);
            }
            else
            {
                SetBusinessSubClassPropertyValue(item, businessType, viewType, businessObject, viewObject);
            }
        }

        /// <summary>
        /// 设置子对象属性的值
        /// </summary>
        private void SetBusinessSubClassPropertyValue(PropertyConfig item, Type businessType, Type viewType, TB businessObject, TV viewObject)
        {
            if (item.IsSubKey)
            {
                //获取BusinessObj子对象属性的属性值
                var businessSubClassPropertyValue = GetBusinessSubClassPropertyValue(item, businessType,
                                                                                        businessObject);
                //获取ViewObject的属性值
                PropertyInfo viewProperty = viewType.GetProperty(item.ViewProperty);
                var viewPropertyValue = viewProperty.GetValue(viewObject, null);
                //重新设置BusinessObject子类型属性的值
                if ((businessSubClassPropertyValue == null && viewPropertyValue != null)
                    || (businessSubClassPropertyValue != null
                        && viewPropertyValue != null
                        && businessSubClassPropertyValue.ToString() != viewPropertyValue.ToString()))
                {
                    PropertyInfo businessProperty = GetBusinessProperty(item, businessType);
                    var businessPropertyValue =
                        SpringHelper.Context.GetObjectById<IConversion>(businessProperty.PropertyType.Name + "Conversion").GetByPrimaryKey(
                            viewPropertyValue.ToString());
                    if (businessPropertyValue != null)
                        businessProperty.SetValue(businessObject, businessPropertyValue, null);
                }
            }
        }

        private void SetNormalBusinessPropertyValue(PropertyConfig item, Type businessType, Type viewType, TB businessObject, TV viewObject)
        {
            //获取BusinessObject和ViewObject的属性值
            PropertyInfo businessProperty = businessType.GetProperty(item.BusinessProperty);
            PropertyInfo viewProperty = viewType.GetProperty(item.ViewProperty);
            var businessPropertyValue = businessProperty.GetValue(businessObject, null);
            var viewPropertyValue = viewProperty.GetValue(viewObject, null);
            //设置BusinessObject属性新值
            if ((businessPropertyValue == null)
                || (viewPropertyValue != null
                    && businessPropertyValue.ToString() != viewPropertyValue.ToString()))
                TransAndSetBusinessObjectValue(businessProperty, viewProperty, businessObject, viewPropertyValue);
        }

        #endregion


        private void TransAndSetBusinessObjectValue(PropertyInfo businessProperty, PropertyInfo viewProperty, TB businessObject, object viewPropertyValue)
        {
            var valueConversion = valueConversionFactory.CreateValueConversion(businessProperty.PropertyType,
                                                                               viewProperty.PropertyType);
            var businessValue = valueConversion.GetBusinessValue(viewPropertyValue);

            businessProperty.SetValue(businessObject, businessValue, null);
        }

        private void TransAndSetViewObjectValue(PropertyInfo viewProperty, PropertyInfo businessProperty, TV viewObject, TB businessObject)
        {
            var valueConversion = valueConversionFactory.CreateValueConversion(businessProperty.PropertyType,
                                                                               viewProperty.PropertyType);
            var businessValue = businessProperty.GetValue(businessObject, null);
            var viewValue = valueConversion.GetViewValue(businessValue);

            viewProperty.SetValue(viewObject, viewValue, null);
        }
    }
}